﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace OCRTheNewHope
{
    public class Forest
    {
        public Random rnd = new Random();
        public Digit[] Inputs { get; private set; }
        
        public int NumTree { get; private set; }
        public int NumImagesEachTree { get; private set; }
        
        DecisionTree[] Trees;
        
        public int TestError { get; private set; }
        public double TestAccuracy { get; private set; }
        
        public Forest()
        {
            this.NumTree = Parameters.NumTree;
            this.NumImagesEachTree = Parameters.NumImagesEachTree;
            this.Trees = new DecisionTree[this.NumTree];

            Digit[] testDigits = FileOperations.ReadDatabase(Parameters.TestPath, 0, -1, Parameters.SelectedDigits);

            for (int i = 0; i < this.NumTree; i++)
            {
                Digit[] trainDigits = FileOperations.ReadDatabaseRandomly(Parameters.TrainPath, this.NumImagesEachTree, Parameters.SelectedDigits);
                Console.WriteLine("Training of Tree " + (i + 1) + "/" + this.NumTree + " is started.");
                DecisionNode.CurrentGlobalLevel = 0;
                this.Trees[i] = new DecisionTree(this, trainDigits, testDigits, i);                
                trainDigits = null;

                using (StreamWriter writer = File.AppendText(Parameters.GlobalLogPath))
                {
                    writer.WriteLine(this.Trees[i].LogString);
                }

                Console.WriteLine("Tree " + (i + 1) + "/" + this.NumTree + " is trained.");
                
                this.TestError = this.Test(testDigits);
                this.TestAccuracy = ((double)(Parameters.TotalTestDigits - this.TestError) / Parameters.TotalTestDigits) * 100;

                this.Log();
            }           
            
            Console.WriteLine("Log file is created to: " + Parameters.GlobalLogPath);
        }

        public byte Compute(Digit digit)
        {
            double[] histogram = new double[10];

            for (int i = 0; i < this.Trees.Length; i++)
            {
                if (this.Trees[i] != null)
                {
                    int temp = this.Trees[i].Compute(digit);
                    histogram[temp]++;
                }
            }

            int majority = histogram.MaxIndex();

            return (byte)majority;
        }
        
        public int Test(Digit[] testDigits)
        {
            int numTrue = 0;
            int numFalse = 0;

            for (int i = 0; i < testDigits.Length; i++)
            {
                byte temp = this.Compute(testDigits[i]);

                if (temp == testDigits[i].DigitValue)
                    numTrue++;
                else
                    numFalse++;
            }

            return numFalse;
        }

        public void Log()
        {
            using (StreamWriter writer = File.AppendText(Parameters.GlobalLogPath))
            {
                writer.WriteLine("Forest test error  : " + this.TestError + "/" + Parameters.TotalTestDigits + " ( " + this.TestAccuracy + "% Accuracy )");
                writer.WriteLine();
            }
        }
    }
}
